package com.sunlake.spring.main.model.counselor.service.impl;

import com.sunlake.spring.main.common.response.R;
import com.sunlake.spring.main.common.utils.CurrentUserUtil;
import com.sunlake.spring.main.common.utils.JwtTokenUtil;
import com.sunlake.spring.main.common.utils.LoggerUtil;
import com.sunlake.spring.main.model.auth.mapper.UsersMapper;
import com.sunlake.spring.main.model.auth.po.UsersPO;
import com.sunlake.spring.main.model.counselor.dto.ListRecentDTO;
import com.sunlake.spring.main.model.counselor.dto.ResumeMergeDTO;
import com.sunlake.spring.main.common.model.file.mapper.FilesMapper;
import com.sunlake.spring.main.model.counselor.mapper.ResumesMapper;
import com.sunlake.spring.main.model.counselor.po.FilesPO;
import com.sunlake.spring.main.model.counselor.po.ResumesPO;
import com.sunlake.spring.main.model.counselor.service.CounselorApplyService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;

@Service
public class CounselorApplyServiceImpl implements CounselorApplyService, InitializingBean {

    private static final Logger log = LoggerUtil.getLogger(CounselorApplyServiceImpl.class);

    // 文件存储路径，从配置文件读取
    @Value("${file.upload-dir}")
    private String UPLOAD_DIR;

    @Value("${file.access-prefix}")
    private String ACCESS_PREFIX;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private FilesMapper filesMapper;

    @Autowired
    private ResumesMapper resumesMapper;

    @Autowired
    private UsersMapper usersMapper;

    // 初始化时规范化路径并确保基础目录存在
    @Override
    public void afterPropertiesSet() throws Exception {
        // 规范化路径，确保在不同环境下都能正确解析
        Path normalizedPath = Paths.get(UPLOAD_DIR).toAbsolutePath().normalize();
        UPLOAD_DIR = normalizedPath.toString();

        // 确保基础上传目录存在
        File baseDir = new File(UPLOAD_DIR);
        if (!baseDir.exists() && !baseDir.mkdirs()) {
            throw new IOException("Failed to create base upload directory: " + UPLOAD_DIR);
        }

        // 确保chunks子目录存在
        File chunksDir = new File(UPLOAD_DIR + File.separator + "chunks");
        if (!chunksDir.exists() && !chunksDir.mkdirs()) {
            throw new IOException("Failed to create chunks directory: " + chunksDir.getAbsolutePath());
        }
        log.info("Initialized upload directories: base='{}'", UPLOAD_DIR);
    }

    @Override
    public R<Map<String, Object>> uploadResume(MultipartFile file) throws IOException {
        // 确保上传目录存在（即使初始化时已创建，再次检查以确保安全）
        File directory = new File(UPLOAD_DIR);
        if (!directory.exists() && !directory.mkdirs()) {
            return R.error(500, "无法创建上传目录: " + UPLOAD_DIR);
        }

        // 获取当前登录用户ID和用户名
        Long userId = CurrentUserUtil.getCurrentUserId();
        if (userId == null) {
            return R.error(401, "未登录，无法上传文件");
        }

        // 从请求头中获取token并提取用户名
        String username = getCurrentUsername();
        if (username == null) username = "unknown";

        String originalFilename = file.getOriginalFilename();
        String extension = originalFilename != null && originalFilename.lastIndexOf('.') >= 0 ? originalFilename.substring(originalFilename.lastIndexOf('.')) : ".pdf";
        String fileName = username + "_" + userId + "_" + System.currentTimeMillis() + extension;

        // 保存文件 - 使用File.separator确保路径分隔符兼容
        String filePath = UPLOAD_DIR + File.separator + fileName;

        try { file.transferTo(new File(filePath)); } catch (IOException e) {
            log.error("保存简历文件失败: {}", e.getMessage(), e);
            return R.error(500, "文件保存失败: " + e.getMessage());
        }

        // 返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("fileName", fileName);
        result.put("filePath", ACCESS_PREFIX + File.separator + fileName);
        result.put("fileSize", file.getSize());
        log.info("简历上传成功 userId={}, fileName={}, size={}", userId, fileName, file.getSize());
        return R.ok(200, "文件上传成功", result);
    }

    @Override
    public R<Map<String, Object>> uploadResumeChunk(MultipartFile chunk, String md5, Integer index) throws IOException {
        // 检查必要参数
        if (chunk == null || chunk.isEmpty()) {
            return R.error(400, "分片文件不能为空");
        }

        // 为可选参数提供默认值
        if (md5 == null) md5 = "default_md5";
        if (index == null) index = 0;

        // 获取当前登录用户ID和用户名
        Long userId = CurrentUserUtil.getCurrentUserId();
        if (userId == null) {
            return R.error(401, "未登录，无法上传分片文件");
        }

        // 确保分片临时目录存在 - 这是修复的关键部分
        String chunkDirPath = UPLOAD_DIR + File.separator + "chunks" + File.separator + md5;
        File chunkDir = new File(chunkDirPath);
        if (!chunkDir.exists() && !chunkDir.mkdirs()) {
            return R.error(500, "无法创建分片目录: " + chunkDirPath);
        }

        // 保存分片文件
        File chunkFile = new File(chunkDirPath + File.separator + index + ".part");
        try { chunk.transferTo(chunkFile); } catch (IOException e) {
            log.error("保存分片失败 identifier={}, index={}, error={}", md5, index, e.getMessage(), e);
            return R.error(500, "分片文件保存失败: " + e.getMessage());
        }

        Map<String, Object> result = new HashMap<>();
        result.put("identifier", md5);
        result.put("chunkIndex", index);
        result.put("chunkSize", chunk.getSize());
        log.debug("分片上传成功 identifier={}, index={}, size={}", md5, index, chunk.getSize());
        return R.ok(200, "分片上传成功", result);
    }

    @Override
    public R<Map<String, Object>> mergeResume(ResumeMergeDTO mergeDTO) throws IOException {
        // 检查必要参数
        if (mergeDTO == null) {
            return R.error(400, "合并参数不能为空");
        }

        String identifier = Optional.ofNullable(mergeDTO.getFileId()).orElse(Optional.ofNullable(mergeDTO.getMd5()).orElse("default_identifier"));

        Integer totalChunks = mergeDTO.getTotalChunks();
        if (totalChunks == null || totalChunks <= 0) {
            return R.error(400, "无效的分片数量");
        }

        Long userId = CurrentUserUtil.getCurrentUserId();
        if (userId == null) {
            return R.error(401, "未登录，无法合并文件");
        }
        String username = Optional.ofNullable(getCurrentUsername()).orElse("unknown");

        // 获取分片文件目录
        String chunkDirPath = UPLOAD_DIR + File.separator + "chunks" + File.separator + identifier;
        File chunkDir = new File(chunkDirPath);

        // 验证所有分片是否上传完成
        if (!chunkDir.exists()) {
            return R.error(400, "分片文件目录不存在: " + chunkDirPath);
        }

        File[] chunkFiles = chunkDir.listFiles((dir, name) -> name.endsWith(".part"));
        if (chunkFiles == null || chunkFiles.length != totalChunks) {
            return R.error(400, "分片文件不完整");
        }

        String originalFilename = mergeDTO.getFileName();
        String extension = originalFilename != null && originalFilename.lastIndexOf('.') >= 0 ? originalFilename.substring(originalFilename.lastIndexOf('.')) : ".pdf";
        String mergedFileName = username + "_" + userId + "_" + System.currentTimeMillis() + extension;
        String mergedFilePath = UPLOAD_DIR + File.separator + mergedFileName;

        // 确保目标目录存在
        File mergedFileDir = new File(UPLOAD_DIR);
        if (!mergedFileDir.exists() && !mergedFileDir.mkdirs()) {
            return R.error(500, "无法创建目标文件目录");
        }
        long mergedFileSize = 0L;
        // 合并文件
        try (FileOutputStream outputStream = new FileOutputStream(mergedFilePath)) {
            for (int i = 0; i < totalChunks; i++) {
                File chunkFile = new File(chunkDirPath + File.separator + i + ".part");
                if (!chunkFile.exists()) {
                    return R.error(400, "第" + i + "个分片文件不存在");
                }
                byte[] bytes = java.nio.file.Files.readAllBytes(chunkFile.toPath());
                mergedFileSize += bytes.length;
                outputStream.write(bytes);
            }
            outputStream.flush();
        } catch (IOException e) {
            log.error("合并分片失败 identifier={}, error={}", identifier, e.getMessage(), e);
            return R.error(500, "文件合并失败: " + e.getMessage());
        }
        FilesPO filesPO = new FilesPO();
        filesPO.setFilename(mergedFileName);
        filesPO.setFileUrl(mergedFilePath);
        filesPO.setFileSize(mergedFileSize);
        filesPO.setCreatedAt(LocalDateTime.now());
        filesPO.setUploaderId(Math.toIntExact(userId));
        filesMapper.insert(filesPO);
        FilesPO insertedFile = filesMapper.selectByUploaderId(userId);
        ResumesPO resumesPO = new ResumesPO();
        resumesPO.setUserId(Math.toIntExact(userId));
        resumesPO.setFileId(insertedFile.getId());
        resumesPO.setVersion(1);
        resumesPO.setIsCurrent(1);
        resumesPO.setVisibility("private");
        resumesPO.setResumeUid(UUID.randomUUID().toString());
        resumesMapper.insert(resumesPO);
        Map<String, Object> result = new HashMap<>();
        result.put("fileName", mergedFileName);
        result.put("filePath", ACCESS_PREFIX + File.separator + mergedFileName);
        result.put("originalFilename", originalFilename);
        log.info("分片合并成功 userId={}, identifier={}, fileName={}, size={}", userId, identifier, mergedFileName, mergedFileSize);
        return R.ok(200, "文件合并成功", result);
    }

    /**
     * 从请求头中获取token并提取当前用户的用户名
     */
    private String getCurrentUsername() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                String tokenHeader = request.getHeader("Authorization");
                if (tokenHeader != null && tokenHeader.startsWith("Bearer ")) {
                    String token = tokenHeader.substring(7).trim();
                    return jwtTokenUtil.getUsernameFromToken(token);
                }
            }
        } catch (Exception e) {
            log.warn("解析当前用户名失败: {}", e.getMessage());
        }
        return null;
    }

    public R<Map<String, Object>> getResumesByEmail(ListRecentDTO listRecentDTO) {
        if (listRecentDTO == null || listRecentDTO.getEmail() == null || listRecentDTO.getEmail().isEmpty()) {
            return R.error(400, "email不能为空");
        }
        // 确保用户登录
        UsersPO user = usersMapper.selectByEmail(listRecentDTO.getEmail());
        if (user == null) {
            return R.error(404, "用户不存在");
        }

        // 得到简历表
        ArrayList<ResumesPO> resumesList = resumesMapper.selectByUserId(user.getId());

        ResumesPO resumes = null;

        for(ResumesPO r:resumesList){
            if(r.getIsCurrent()==1){
                resumes = r;
                break;
            }
        }

        if (resumes == null) {
            return R.error(404, "该用户暂无当前简历记录");
        }

        Long fileId = resumes.getFileId();
        if (fileId == null) {
            return R.error(404, "该用户暂无上传的简历文件");
        }
        // 关联Files表
        FilesPO filePO = filesMapper.selectByPrimaryKey(fileId);
        if (filePO == null) {
            return R.error(404, "文件记录不存在");
        }

        String filename = filePO.getFilename();
        Path physicalPath = Paths.get(UPLOAD_DIR, filename).toAbsolutePath().normalize();
        boolean exists = physicalPath.toFile().exists();

        String url = ACCESS_PREFIX + "/" + filename;

        Map<String, Object> data = new HashMap<>();

        data.put("fileName", filename);
        data.put("fileUrl", url);
        data.put("exists", exists);
        data.put("userId", user.getId());
        data.put("resumeId", resumes.getId());
        data.put("userName", user.getUsername());
        data.put("createdAt", resumes.getCreatedAt());
        log.debug("获取简历元数据成功 userId={}, resumeId={}, fileName={}, exists={}", user.getId(), resumes.getId(), filename, exists);
        return R.ok(200, "查询成功", data);
    }

    /**
     * 获取简历文件的元信息
     */
    @Override
    public Map<String, Object> getResumeMeta(Long resumeId) {
        Map<String, Object> meta = new HashMap<>();
        if (resumeId == null) {
            meta.put("acceptRanges", false);
            meta.put("totalSize", null);
            return meta;
        }
        ResumesPO resumesPO = resumesMapper.selectByPrimaryKey(resumeId);
        if (resumesPO == null) {
            meta.put("acceptRanges", false);
            meta.put("totalSize", null);
            return meta;
        }
        Long fileId = resumesPO.getFileId();
        if (fileId == null) {
            meta.put("acceptRanges", false);
            meta.put("totalSize", null);
            return meta;
        }
        FilesPO filePO = filesMapper.selectByPrimaryKey(fileId);
        if (filePO == null) {
            meta.put("acceptRanges", false);
            meta.put("totalSize", null);
            return meta;
        }
        String filename = filePO.getFilename();
        String physicalPath = filePO.getFileUrl() != null ? filePO.getFileUrl() : (UPLOAD_DIR + File.separator + filename);
        File physicalFile = new File(physicalPath);
        if (!physicalFile.exists()) {
            meta.put("acceptRanges", false);
            meta.put("totalSize", null);
            return meta;
        }
        meta.put("acceptRanges", true);
        meta.put("totalSize", physicalFile.length());
        meta.put("contentType", detectContentType(filename));
        meta.put("fileName", filename);
        return meta;
    }

    /**
     * 整文件下载
     */
    @Override
    public void downloadResume(Long resumeId, HttpServletResponse response) throws IOException {
        ResumesPO resumesPO = resumesMapper.selectByPrimaryKey(resumeId);
        if (resumesPO == null) { writeJsonError(response, 404, "简历不存在"); return; }
        Long fileId = resumesPO.getFileId();
        if (fileId == null) { writeJsonError(response, 404, "文件记录不存在"); return; }
        FilesPO filePO = filesMapper.selectByPrimaryKey(fileId);
        if (filePO == null) { writeJsonError(response, 404, "文件记录不存在"); return; }
        String filename = filePO.getFilename();
        File physicalFile = new File(filePO.getFileUrl() != null ? filePO.getFileUrl() : (UPLOAD_DIR + File.separator + filename));
        if (!physicalFile.exists()) { writeJsonError(response, 404, "文件不存在"); return; }
        response.setStatus(200);
        response.setContentType(detectContentType(filename));
        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Content-Length", String.valueOf(physicalFile.length()));
        setContentDisposition(response, filename);
        try (java.io.InputStream in = new java.io.FileInputStream(physicalFile); java.io.OutputStream out = response.getOutputStream()) {
            byte[] buf = new byte[8192]; int len; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); }
            out.flush();
        }
    }

    /**
     * 范围下载（断点续传）
     */
    @Override
    public void downloadResumeRange(Long resumeId, long start, long end, HttpServletResponse response) throws IOException {
        ResumesPO resumesPO = resumesMapper.selectByPrimaryKey(resumeId);
        if (resumesPO == null) { writeJsonError(response, 404, "简历不存在"); return; }
        Long fileId = resumesPO.getFileId();
        if (fileId == null) { writeJsonError(response, 404, "文件记录不存在"); return; }
        FilesPO filePO = filesMapper.selectByPrimaryKey(fileId);
        if (filePO == null) { writeJsonError(response, 404, "文件记录不存在"); return; }
        String filename = filePO.getFilename();
        File physicalFile = new File(filePO.getFileUrl() != null ? filePO.getFileUrl() : (UPLOAD_DIR + File.separator + filename));
        if (!physicalFile.exists()) { writeJsonError(response, 404, "文件不存在"); return; }
        long fileLength = physicalFile.length();
        // 规范化范围
        if (start < 0) start = 0;
        if (end < 0 || end >= fileLength) end = fileLength - 1;
        if (start > end) { writeJsonError(response, 416, "范围无效"); return; }
        long contentLength = end - start + 1;
        response.setStatus(206);
        response.setContentType(detectContentType(filename));
        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Content-Length", String.valueOf(contentLength));
        response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
        setContentDisposition(response, filename);
        try (RandomAccessFile raf = new RandomAccessFile(physicalFile, "r"); java.io.OutputStream out = response.getOutputStream()) {
            raf.seek(start);
            byte[] buf = new byte[8192]; long remaining = contentLength; while (remaining > 0) { int read = raf.read(buf, 0, (int) Math.min(buf.length, remaining)); if (read == -1) break; out.write(buf, 0, read); remaining -= read; }
            out.flush();
        }
    }

    // ================== 辅助方法 ==================
    private void writeJsonError(HttpServletResponse response, int status, String message) throws IOException {
        response.setStatus(status);
        response.setContentType("application/json;charset=UTF-8");
        String json = "{\"success\":false,\"code\":" + status + ",\"message\":\"" + message + "\"}";
        response.getWriter().write(json);
    }

    private void setContentDisposition(HttpServletResponse response, String filename) {
        try {
            String encoded = URLEncoder.encode(filename, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + encoded + "\"; filename*=UTF-8''" + encoded);
        } catch (Exception e) {
            log.debug("设置Content-Disposition失败: {}", e.getMessage());
        }
    }

    private String detectContentType(String filename) {
        if (filename == null) return "application/octet-stream";
        String lower = filename.toLowerCase(Locale.ROOT);
        if (lower.endsWith(".pdf")) return "application/pdf";
        if (lower.endsWith(".docx")) return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
        if (lower.endsWith(".doc")) return "application/msword";
        if (lower.endsWith(".txt")) return "text/plain";
        return "application/octet-stream";
    }
}
